#include "irtree.hpp"
#include "Visitor.hpp"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

static unsigned int genericTemp = 0, genericLabel = 0;

/********************************************************************
*              Implementação da Classe StmtFragmentList             *
********************************************************************/

StmtFragmentList :: StmtFragmentList(StmtFragment* stmFrag, StmtFragmentList* nextStm){
    this->nextStmFrag = nextStm;
    this->stmFrag = stmFrag;
}

StmtFragmentList :: ~StmtFragmentList(){
    if(this->nextStmFrag) delete this->nextStmFrag;
    if(this->stmFrag) delete this->stmFrag;
}

int StmtFragmentList :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

void StmtFragmentList :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Access_List                  *
********************************************************************/

Access_List :: Access_List(Local_Access* access, Access_List* next){
    this->access = access;
    this->next = next;
}

Access_List :: ~Access_List(){
    if(this->access) delete this->access;
    if(this->next) delete this->next;
}

int Access_List :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

void Access_List :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Label                        *
********************************************************************/

Label :: Label(char* name){
    this->Label_Name = (char*)malloc((strlen(name)+1)*sizeof(char));
    strcpy(this->Label_Name, name);
}

Label :: Label(){
    char aux[1000];
    sprintf(aux,"LABEL%d",genericLabel);
    this->Label_Name = (char*)malloc((strlen(aux)+1)*sizeof(char));
    strcpy(this->Label_Name, aux);
    genericLabel++;
}

Label :: ~Label(){
    if(this->Label_Name) free(this->Label_Name);
}

int Label :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Label :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Temp                         *
********************************************************************/

Temp :: Temp(char* name){
    this->name = (char*)malloc((strlen(name)+1)*sizeof(char));
    strcpy(this->name, name);
}

Temp :: Temp(){
    char aux[1000];
    sprintf(aux, "TEMP$%d", genericTemp);
    this->name = (char*)malloc((strlen(aux)+1)*sizeof(char));
    strcpy(this->name, aux);
    genericTemp++;
}

Temp :: ~Temp(){
    if(this->name) free(this->name);
}

int Temp :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Temp :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Label_List                   *
********************************************************************/

Label_List :: Label_List(Label* name, Label_List* next){
    this->label = name;
    this->next = next;
}

Label_List :: ~Label_List(){
    if(this->label) delete this->label;
    if(this->next) delete this->next;
}

int Label_List :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Label_List :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Temp_List                    *
********************************************************************/

Temp_List :: Temp_List(Temp* temp, Temp_List* next){
    this->next = next;
    this->temp = temp;
}

Temp_List :: ~Temp_List(){
    if(this->next) delete this->next;
    if(this->temp) delete this->temp;
}

int Temp_List :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Temp_List :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe FragmentList                 *
********************************************************************/

FragmentList :: FragmentList(Fragment* frag, FragmentList* nextFrag){
    this->frag = frag;
    this->nextFrag = nextFrag;
}

int FragmentList :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

FragmentList :: ~FragmentList(){
    if(this->frag) delete this->frag;
    if(this->nextFrag) delete this->nextFrag;
}

void FragmentList :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Method                       *
********************************************************************/

Method :: Method(Frame* f, StmtFragmentList* body){
    this->body = body;
    this->frame = f;
}

Method :: ~Method(){
    if(this->body) delete this->body;
    if(this->frame) delete this->frame;
}

int Method :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Method :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe Literal_Frag                 *
********************************************************************/
Literal_Frag :: Literal_Frag(char* literal){
    this->label_literal = new Label(literal);
}

Literal_Frag :: ~Literal_Frag(){
    if(this->label_literal) delete this->label_literal;
}

int Literal_Frag :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void Literal_Frag :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe InFrame                      *
********************************************************************/
InFrame :: InFrame(int offset, int type, bool isParam, char* name){
    this->offset = offset;
    this->isParam = isParam;
    this->type = type;
    if(name){
        this->name = (char*)malloc((strlen(name)+1)*sizeof(char));
        strcpy(this->name, name);
    }else this->name = name;
}

InFrame :: ~InFrame(){
    if(this->name) free(this->name);
}

StmtFragment* InFrame :: access_code(){
    if(this->isParam) return (StmtFragment*) new IRNBinop(BINOP_PLUS, new IRNTemp(new Temp((char*)"$fp")), new IRNConst(this->offset));
    else return (StmtFragment*) new IRNBinop(BINOP_SUB, new IRNTemp(new Temp((char*)"$fp")), new IRNConst(this->offset));
}

int InFrame :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void InFrame :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe InReg                        *
********************************************************************/
InReg :: InReg(Temp* temp, int type, bool isParam, char* name){
    this->isParam = isParam;
    this->temp = temp;
    this->type = type;
    if(name){
        this->name = (char*)malloc((strlen(name)+1)*sizeof(char));
        strcpy(this->name, name);
    }else this->name = name;
}

InReg :: ~InReg(){
    if(this->temp) delete this->temp;
    if(this->name) free(name);
}

StmtFragment* InReg :: access_code(){
    return (StmtFragment*) new IRNTemp(this->temp);
}

int InReg :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void InReg :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe FrameMIPS                        *
********************************************************************/

FrameMIPS :: FrameMIPS(){
    this->offSetAmount = 4;
    this->offSetParamAmount = 4;
    this->number_of_params = 0;
    this->param_size = 0;
    this->returnReg = new Temp();
    this->label = new Label();
    this->returnAddress = new Label();
    this->access_list = new Access_List(NULL, NULL);
    this->tempList = new Temp_List(NULL, NULL);
}

FrameMIPS :: ~FrameMIPS(){
    if(this->returnReg) delete this->returnReg;
    if(this->access_list) delete this->access_list;
    if(this->label) delete this->label;
}

void FrameMIPS :: addLocal(bool escape, int tambytes, char* varName, int type){
    if(escape){
        InFrame *var = new InFrame(this->offSetAmount, type, false, varName);
        if(!this->access_list->access) this->access_list->access = (Local_Access*) var;
        else{
            Access_List *Next = new Access_List((Local_Access*)var, NULL);
            Access_List *aux = this->access_list;
            while(aux->next) aux = aux->next;
            aux->next = Next;
        }
        this->offSetAmount += tambytes;
    }else{
        Temp *t = new Temp();
        InReg *var = new InReg(t, type, false, varName);
        if(!this->tempList->temp) this->tempList->temp = t;
        else{
            Temp_List *Next = new Temp_List(t, NULL);
            Temp_List *aux = this->tempList;
            while(aux->next) aux = aux->next;
            aux->next = Next;
        }
        if(!this->access_list->access) this->access_list->access = (Local_Access*) var;
        else{
            Access_List *Next = new Access_List((Local_Access*)var, NULL);
            Access_List *aux = this->access_list;
            while(aux->next) aux = aux->next;
            aux->next = Next;
        }
    }
}

void FrameMIPS :: addParam(bool escape, int tambytes, char* paramName, int type){
    if(this->number_of_params <= 4){
        if(escape){
            InFrame *param = new InFrame(this->offSetParamAmount, type, true, paramName);
            if(!this->access_list->access) this->access_list->access = (Local_Access*) param;
            else{
                Access_List *Next = new Access_List((Local_Access*)param, NULL);
                Access_List *aux = this->access_list;
                while(aux->next) aux = aux->next;
                aux->next = Next;
            }
            this->offSetParamAmount += tambytes;
        }else{
            Temp *t = new Temp();
            InReg *param = new InReg(t, type, true, paramName);
            if(!this->tempList->temp) this->tempList->temp = t;
            else{
                Temp_List *Next = new Temp_List(t, NULL);
                Temp_List *aux = this->tempList;
                while(aux->next) aux = aux->next;
                aux->next = Next;
            }
            if(!this->access_list->access) this->access_list->access = (Local_Access*) param;
            else{
                Access_List *Next = new Access_List((Local_Access*)param, NULL);
                Access_List *aux = this->access_list;
                while(aux->next) aux = aux->next;
                aux->next = Next;
            }
        }
    }else{
        InFrame *param = new InFrame(this->offSetParamAmount, type, true, paramName);
        if(!this->access_list->access) this->access_list->access = (Local_Access*) param;
        else{
            Access_List *Next = new Access_List((Local_Access*)param, NULL);
            Access_List *aux = this->access_list;
            while(aux->next) aux = aux->next;
            aux->next = Next;
        }
        this->offSetParamAmount += tambytes;
    }
    this->param_size += tambytes;
    this->number_of_params++;
}

int FrameMIPS :: acceptVisitor(VisitorIRT* v){
    return v->visit(this);
}

void FrameMIPS :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNStmList                   *
********************************************************************/
IRNStmList :: IRNStmList(IRNStm *Stm, IRNStmList *StmList){
    this->stm = Stm;
    this->tail = StmList;
}

IRNStmList :: ~IRNStmList(){
    if(this->stm) delete this->stm;
    if(this->tail) delete this->tail;
}

int IRNStmList :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

void IRNStmList :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNExpList                   *
********************************************************************/
IRNExpList :: IRNExpList(IRNExp *Exp, IRNExpList *ExpList){
    this->head = Exp;
    this->tail = ExpList;
}

IRNExpList :: ~IRNExpList(){
    if(this->head) delete this->head;
    if(this->tail) delete this->tail;
}

int IRNExpList :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

void IRNExpList :: acceptVisitor(VisitorIRTCanonizer *v){
    v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNConst                     *
********************************************************************/
IRNConst :: IRNConst(int Value){
    this->value = Value;
}

IRNConst :: ~IRNConst(){}

int IRNConst :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNConst :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}



/********************************************************************
*              Implementação da Classe IRNConst                     *
********************************************************************/
IRNConstf :: IRNConstf(double Value){
    this->value = Value;
}

IRNConstf :: ~IRNConstf(){}

int IRNConstf :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNConstf :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}



/********************************************************************
*              Implementação da Classe IRNName                      *
********************************************************************/
IRNName :: IRNName(Label *lab){
    this->label = lab;
}

IRNName :: ~IRNName(){
    if(this->label) delete this->label;
}

int IRNName :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNName :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNTemp                      *
********************************************************************/
IRNTemp ::IRNTemp(Temp *tmp){
    this->temp = tmp;
}

IRNTemp :: ~IRNTemp(){
    if(this->temp) delete this->temp;
}

int IRNTemp :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNTemp :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNBinop                     *
********************************************************************/
IRNBinop :: IRNBinop(int op, IRNExp *Left, IRNExp *Right){
    this->binop = op;
    this->left = Left;
    this->right = Right;
}

IRNBinop :: ~IRNBinop(){
    if(this->left) delete this->left;
    if(this->right) delete this->right;
}

int IRNBinop :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNBinop :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNMen                       *
********************************************************************/
IRNMem :: IRNMem(IRNExp *Exp){
    this->exp = Exp;
}

IRNMem :: ~IRNMem(){
    if(this->exp) delete this->exp;
}

int IRNMem :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNMem :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNCall                      *
********************************************************************/
IRNCall :: IRNCall(IRNExp *Exp, IRNExpList *ExpList){
    this->func = Exp;
    this->args = ExpList;
}

IRNCall :: ~IRNCall(){
    if(this->func) delete this->func;
    if(this->args) delete this->args;
}

int IRNCall :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNCall :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNEseq                      *
********************************************************************/
IRNEseq :: IRNEseq(IRNStm *Stm, IRNExp *Exp){
    this->stm = Stm;
    this->exp = Exp;
}

IRNEseq :: ~IRNEseq(){
    if(this->stm) delete this->stm;
    if(this->exp) delete this->exp;
}

int IRNEseq :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNExp* IRNEseq :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNMove                      *
********************************************************************/
IRNMove :: IRNMove(IRNExp *Dst, IRNExp *Src){
    this->destination = Dst;
    this->source = Src;
}

IRNMove :: ~IRNMove(){
    if(this->destination) delete this->destination;
    if(this->source) delete this->source;
}

int IRNMove :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNMove :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNExpStm                    *
********************************************************************/
IRNExpStm :: IRNExpStm(IRNExp *Exp){
    this->exp = Exp;
}

IRNExpStm :: ~IRNExpStm(){
    if(this->exp) delete this->exp;
}

int IRNExpStm :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNExpStm :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNJump                      *
********************************************************************/
IRNJump :: IRNJump(IRNExp *Exp){
    this->exp = Exp;
}

IRNJump :: ~IRNJump(){
    if(this->exp) delete this->exp;
}

int IRNJump :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNJump :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNCJump                     *
********************************************************************/
IRNCJump :: IRNCJump(int op, IRNExp *left, IRNExp *right, Label *Iftrue, Label *Iffalse){
    this->relOp = op;
    this->left = left;
    this->right = right;
    this->iftrue = Iftrue;
    this->iffalse = Iffalse;
}

IRNCJump :: ~IRNCJump(){
    if(this->left) delete this->left;
    if(this->right) delete this->right;
    if(this->iftrue) delete this->iftrue;
    if(this->iffalse) delete this->iffalse;
}

int IRNCJump :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNCJump :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNSeq                       *
********************************************************************/
IRNSeq :: IRNSeq(IRNStm *Left, IRNStm *Right){
    this->left = Left;
    this->right = Right;
}

IRNSeq :: ~IRNSeq(){
    if(this->left) delete this->left;
    if(this->right) delete this->right;
}

int IRNSeq :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNSeq :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}


/********************************************************************
*              Implementação da Classe IRNLabel                     *
********************************************************************/
IRNLabel :: IRNLabel(Label *Lab){
    this->label = Lab;
}

IRNLabel :: ~IRNLabel(){
    if(this->label) delete this->label;
}

int IRNLabel :: acceptVisitor(VisitorIRT *v){
    return v->visit(this);
}

IRNStm* IRNLabel :: acceptVisitor(VisitorIRTCanonizer *v){
    return v->visit(this);
}
